package com.xtpt.disaster.controller;

import com.alibaba.fastjson.JSON;
import com.xtpt.disaster.bean.*;
import com.xtpt.disaster.common.PortalResult;
import com.xtpt.disaster.common.PropertyConfig;
import com.xtpt.disaster.common.SQLServer.CallbackUrlGenerator;
import com.xtpt.disaster.common.SQLServer.SQLServerCmdBuilder;
import com.xtpt.disaster.common.SQLServer.SQLServerCommandGenerator;
import com.xtpt.disaster.common.consts.DBEnum;
import com.xtpt.disaster.common.consts.StatusConst;
import com.xtpt.disaster.common.consts.TaskTypeEnum;
import com.xtpt.disaster.controller.VO.SQLServerRecoverInfo;
import com.xtpt.disaster.service.DBNodeInfoService;
import com.xtpt.disaster.service.JobDetailService;
import com.xtpt.disaster.service.NodeInfoManageService;
import com.xtpt.disaster.service.TaskInfoManageService;
import com.xtpt.disaster.utils.IDGenerator;
import com.xtpt.disaster.utils.LogUtil;
import com.xtpt.disaster.utils.QuartzSendBack;
import com.xtpt.disaster.utils.TimeUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.nio.file.Paths;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author jack_xin
 * @ClassName SQLServerController
 * @Desc SQLServer备份恢复控制器
 * @date 2021年12月06日 18:01
 * @Version TODO
 */
@RestController
@CrossOrigin
@Api(tags = "SQLServer数据库接口")
@RequestMapping(value = "/sqlserver")
public class SQLServerController {
    @Autowired
    DBNodeInfoService dbNodeInfoService;

    @Autowired
    PropertyConfig config;

    @Autowired
    NodeInfoManageService nodeInfoManageService;

    @Autowired
    JobDetail jobDetail;

    @Autowired
    JobDetailService jobDetailService;

    @Autowired
    CallbackUrlGenerator generator;

    @Autowired
    SQLServerCmdBuilder sqlServerCmdBuilder;

    @Autowired
    SQLServerRecoverInfo sqlServerRecoverInfo;

    @Autowired
    ActionParameters actionParameters;

    @Autowired
    QuartzSendBack quartzSendBack;

    @Autowired
    TaskInfoManageService taskInfoManageService;

    @ApiOperation(tags = "SQLServer", value = "/submit", httpMethod = "POST", produces = "application/json")
    @PostMapping("/submit")
    public PortalResult sqlServerTaskSubmit(@RequestBody TaskInfo taskInfo) {
        try{
            Long taskId = IDGenerator.longid();
            taskInfo.setTaskId(taskId);

            Long dbId = taskInfo.getDbId();
            DbnodeInfo dbnodeInfo = dbNodeInfoService.selectByPrimaryKey(dbId);

            Integer dbType = dbnodeInfo.getNodeDbType();
            PortalResult result = taskInfoManageService.insertTaskInfo(taskInfo);

            if (StatusConst.ERROR.equals(result.getStatus())) return result;
            if (!dbType.equals(DBEnum.SQLSERVER.getValue())) return PortalResult.error("database type error!");

            String taskName = taskInfo.getTaskName();
            Integer taskType = taskInfo.getTaskType();
            Integer taskScheduleType = taskInfo.getTaskExectype();

            String taskUrl = "http://" + config.getServerIp() + ":" + config.getServerPort();
            Long jobId = taskInfo.getJobDetailId();
            if (jobId!=null){
                sqlServerRecoverInfo.setJobDetailId(jobId);
                sqlServerRecoverInfo.setTaskName(taskName);
                sqlServerRecoverInfo.setJobType(taskType);
                sqlServerRecoverInfo.setScheduleType(taskScheduleType);
            }

            actionParameters.setRequestMethod("POST");
            if (taskType.equals(TaskTypeEnum.RECOVER.getValue())) {
                taskUrl += "/sqlserver/recover";
                actionParameters.setRequestUrl(taskUrl);
                actionParameters.setRequestParameters(sqlServerRecoverInfo);
                return quartzSendBack.sqlServerTaskCallBack(taskInfo, actionParameters);
            }
            else if (taskType.equals(TaskTypeEnum.BACKUP.getValue())) {
                taskUrl += "/sqlserver/backup";
                actionParameters.setRequestUrl(taskUrl);
                actionParameters.setRequestParameters(taskInfo);
                return quartzSendBack.sqlServerTaskCallBack(taskInfo, actionParameters);
            }
            else return PortalResult.error("任务类型错误！");

        } catch (Exception e){
            jobDetail.setJobResult(e.getMessage());
            jobDetail.setJobStatus(StatusConst.ERROR);
            return PortalResult.error(e.getMessage());
        }
    }


    @ApiOperation(tags = "SQLServer数据库备份操作", value = "/backup", httpMethod = "POST", produces = "application/json")
    @PostMapping("/backup")
    public PortalResult backupSQLServer(@RequestBody TaskInfo taskInfo) {
        try {
            //获取数据库信息
            Long dbId = taskInfo.getDbId();
            DbnodeInfo dbnodeInfo = dbNodeInfoService.selectByPrimaryKey(dbId);

            //生成jobId，获取数据库信息实体
            Long jobId = IDGenerator.longid();
            Integer dbInfo = dbnodeInfo.getNodeDbType();
            jobDetail.setJobId(jobId);
            jobDetail.setSourceDbType(dbInfo);

            /*
             *生成dmCmdBuilder.dbBackUp方法所需的参数
             */
            Long taskId = taskInfo.getTaskId();
            jobDetail.setTaskId(taskId);
            // 备份拼接文件名
            String startTimeStr = TimeUtil.dateToString(new Date());
            String backupName = startTimeStr + "_" + dbnodeInfo.getNodeDbName();
            String backupType = taskInfo.getTaskBackupType().toString();
            String desc = taskInfo.getTaskDesc() + "(" + jobDetail.getJobId()+")";
            jobDetail.setJobDesc(desc);

            sqlServerCmdBuilder.initializeWithClientNode(dbnodeInfo);

            //生成執行結果回傳接口地址
            String callbackUrl = generator.urlGenerator("/sqlserver/receiver/" + jobId.toString());
            sqlServerCmdBuilder.setResultCallBackUrl(callbackUrl);

            Map<String,String> serverToClientFiles = sqlServerCmdBuilder.dbSQLServerBackUp(
                    taskId.toString(),
                    backupName,
                    backupType,
                    desc,
                    jobId.toString()
            );
            // 将备份命令转化为json字符串
            String cmd = sqlServerCmdBuilder.buildToJSONString();
            System.out.println("提交到客户端！");


            // 保存job发出的cmd记录
            System.out.println(cmd);
            System.out.println(cmd.length());
            jobDetail.setJobCommand(cmd);
            jobDetail.setDataType(taskInfo.getTaskFileType());
            StringBuilder serverFilesPath= new StringBuilder();
            StringBuilder clientFilesPath= new StringBuilder();
            for (Map.Entry<String,String> entry:serverToClientFiles.entrySet()){
                serverFilesPath.append(entry.getKey()).append(",");
                clientFilesPath.append(entry.getValue()).append(",");
            }
            Integer dataType = taskInfo.getTaskFileType();

            Integer jobType = taskInfo.getTaskType();

            Integer jobBackupType = taskInfo.getTaskBackupType();

            Integer scheduleType = taskInfo.getTaskExectype();

            String jobName = taskInfo.getTaskName();
            // 保存服务端路径
            jobDetail.setServerPath(serverFilesPath.deleteCharAt(serverFilesPath.length()-1).toString());
            // 保存客户端路径
            jobDetail.setClientPath(clientFilesPath.deleteCharAt(clientFilesPath.length()-1).toString());
            //
            jobDetail.setDataType(dataType);
            //
            jobDetail.setJobType(jobType);
            //
            jobDetail.setJobBackupType(jobBackupType);
            //
            jobDetail.setScheduleType(scheduleType);
            //
            jobDetail.setJobName(jobName);

            jobDetail.setDbId(dbId);

            Date jobStartTime = TimeUtil.dateMaker();
            jobDetail.setJobStartTime(jobStartTime);

            // 将job记录存入表中
            PortalResult res = jobDetailService.insert(jobDetail);
            if(!res.isOK()) {
                return PortalResult.error("备份记录到作业表中时失败");
            }
            // 将命令提交到代理端执行
            String postToClient = cn.hutool.http.HttpUtil.post(dbnodeInfo.getNodeClientAgentUrl(), cmd);
            System.out.println("提交到客户端成功！");

            jobDetail.setJobResult(postToClient);
            jobDetailService.update(jobDetail);

            PortalResult portalResult = JSON.parseObject(postToClient,PortalResult.class);
            if(!portalResult.getStatus().equals(StatusConst.SUCCESS))
            {
                return PortalResult.error("请求代理客户端失败！");
            }

        } catch (Exception e){
            jobDetail.setJobResult(e.getMessage());
            jobDetail.setJobStatus(StatusConst.ERROR);
            jobDetailService.update(jobDetail);
            e.printStackTrace();
        }
        return PortalResult.error("备份任务执行失败！");
    }


    @ApiOperation(tags = "SQLServer数据库恢复操作", value = "/", httpMethod = "POST", produces = "application/json")
    @PostMapping("/recover")
    public PortalResult recoverSQLServer(@RequestBody SQLServerRecoverInfo recoverInfo) {
        try {
            Long jobId = recoverInfo.getJobDetailId();
            JobDetail jobDetail = jobDetailService.getById(jobId);
            Long newJobId = IDGenerator.longid();

            String clientPath = jobDetail.getClientPath();
            String serverPath = jobDetail.getServerPath();

            Long dbId = jobDetail.getDbId();
            DbnodeInfo dbInfo = dbNodeInfoService.selectByPrimaryKey(dbId);

            sqlServerCmdBuilder.initializeWithClientNode(dbInfo);
            List<String> recoverCommand = SQLServerCommandGenerator.RecoverCommand(dbInfo, clientPath);

            Map<String,String> clientToServerFiles = sqlServerCmdBuilder.dbSQLServerRecover(clientPath,
                    serverPath,
                    recoverCommand);

            String callbackUrl = generator.urlGenerator("/sqlserver/receiver/" + jobId.toString());
            sqlServerCmdBuilder.setResultCallBackUrl(callbackUrl);

            String cmd = sqlServerCmdBuilder.buildToJSONString();
            System.out.println("提交到客户端！");

            Date jobStartTime = TimeUtil.dateMaker();
            jobDetail.setJobStartTime(jobStartTime);
            // 将命令提交到代理端执行
            System.out.println(cmd);
            System.out.println(cmd.length());
            jobDetail.setJobCommand(cmd);

            StringBuilder serverFilesPath= new StringBuilder();
            StringBuilder clientFilesPath= new StringBuilder();
            for (Map.Entry<String,String> entry:clientToServerFiles.entrySet()){
                serverFilesPath.append(entry.getKey()).append(",");
                clientFilesPath.append(entry.getValue()).append(",");
            }
            jobDetail.setJobId(newJobId);

            //
            jobDetail.setJobType(recoverInfo.getJobType());
            //
            jobDetail.setJobBackupType(null);
            //
            jobDetail.setScheduleType(recoverInfo.getScheduleType());
            //
            jobDetail.setJobName(recoverInfo.getTaskName());
            // 将job记录存入表中
            PortalResult res = jobDetailService.insert(jobDetail);
            if(!res.isOK()) {
                return PortalResult.error("备份记录到作业表中时失败");
            }

            String postToClient = cn.hutool.http.HttpUtil.post(dbInfo.getNodeClientAgentUrl(), cmd);
            System.out.println("提交到客户端成功！");

            jobDetail.setJobResult(postToClient);
            jobDetailService.update(jobDetail);
            PortalResult portalResult = JSON.parseObject(postToClient,PortalResult.class);
            if(!portalResult.getStatus().equals(StatusConst.SUCCESS))
            {
                return PortalResult.error("请求代理客户端失败！");
            }

            // 保存job发出的cmd记录

        }catch (Exception e){
            jobDetail.setJobResult(e.getMessage());
            jobDetail.setJobStatus(StatusConst.ERROR);
            e.printStackTrace();
        }
        return PortalResult.error("ren务执行失败！");
    }

    /**
     * @Description: 接收代理客户端任务执行结果
     * @Author: hpc
     * @Date: 2021/12/27 下午 3:13
     * @param jobId: 结果所属作业ID
     * @param resultStr: 结果字符串本身
     * @return: com.xtpt.disaster.common.PortalResult
     **/
    @RequestMapping("/receiver/{jobId}")
    public PortalResult receiver(@PathVariable("jobId") Long jobId, @RequestBody String resultStr)
    {
        if(jobId!=null && !StringUtils.isEmpty(resultStr))
        {
            JobDetail jobDetail=jobDetailService.getById(jobId);
            if(jobDetail==null)
            {
                LogUtil.logger.error("jobId:"+jobId+" 不存在！");
                return PortalResult.error(jobId+" 不存在！");
            }
            try {
                ClientCmdExecResult result= JSON.parseObject(resultStr,ClientCmdExecResult.class);
                jobDetail.setJobResult(JSON.toJSONString(result.getExecCmdResult()));
                jobDetail.setJobEndTime(TimeUtil.dateMaker());
                if(!result.isStatus())
                {
                    LogUtil.logger.error(JSON.toJSONString(result.getErrorCmdList()));
                }
                String serverFilesPath = jobDetail.getServerPath();
                boolean transOK=true;
                if(serverFilesPath!=null)
                {
                    for (String file:serverFilesPath.split(",")) {
                        transOK=cn.hutool.core.io.file.PathUtil.exists(Paths.get(file),true);
                        if(!transOK)
                        {
                            LogUtil.logger.error("文件："+file+"，未传输到服务器！");
                            break;
                        }
                    }
                }
                if(transOK)
                {
                    jobDetail.setJobStatus(StatusConst.SUCCESS);
                }
                else
                {
                    jobDetail.setJobStatus(StatusConst.ERROR);
                }
            }
            catch (Exception ex){
                jobDetail.setJobStatus(StatusConst.ERROR);
                jobDetail.setJobResult(jobDetail.getJobResult()+"\r\nException:"+ex.getMessage());
                LogUtil.logger.error(ex.getMessage());
                ex.printStackTrace();
            }
            Integer update= jobDetailService.updateByPrimaryKeySelective(jobDetail);
            LogUtil.logger.info("更新"+update+"条数据记录！");
            if(jobDetail.getJobStatus().equals(StatusConst.SUCCESS))
            {
                return PortalResult.ok("成功更新"+update+"条数据记录！");
            }
            else if(jobDetail.getJobStatus().equals(StatusConst.ERROR))
            {
                return PortalResult.ok("失败！"+jobDetail.getJobResult());
            }else
            {
                return PortalResult.ok("RUNNING");
            }
        }
        LogUtil.logger.error("结果上报参数不完整！\njobId:"+jobId+"\nresult:"+resultStr);
        return PortalResult.error("结果上报参数不完整！");
    }
}
